#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include "sortCore.h"
#include "kvec.h"

// PartI: target Length   => Offset 


long *targetOffset_ = NULL ; 

int  numTarget_ =  0 ;


typedef struct {
    long coordinate;
    bam1_t *bam ;
} bamItem ;

//typedef kvec_t(bamItem)  bamVect ;
//
//
typedef kvec_t(bam1_t) bamVect ;


bamVect  bamVect_ ;

bam_hdr_t *h_ ;
#if 0

static void  targetOffset( bam_hdr_t *h)
{
    int i = 0 ;
    targetOffset_ =  malloc(sizeof(long)*h->n_targets);
    targetOffset_[0] = 0 ; 
    for( i = 1 ;  i  <  h->n_targets ; i++){
        targetOffset_[i] +=  (h->target_len[i-1] +  targetOffset_[i-1]);
    } 
    numTarget_ = h->n_targets ;
}


static void  targetFree()
{
    free(targetOffset_);
}
#endif

// PartII:sort


void coreInit( char *fn)
{
    kv_init(bamVect_);

    kv_resize(bam1_t,bamVect_, 1024*1024*1024);
//    kv_set(bam1_t,bamVect_);

    samFile *fp =  sam_open(fn,"r");
    h_ =  sam_hdr_read(fp);
    sam_close(fp);
}

void coreDestory()
{
    kv_destroy(bamVect_);
    bam_hdr_destroy(h_);
}

inline long  offset2coordinate( int tid , int pos)
{
    return  targetOffset_[tid] + pos ;
}


void  readBam2Vector( char *fn , int n_threads)
{
    samFile *fp =  sam_open(fn,"r");

    hts_set_threads(fp,n_threads);

    bam_hdr_t *h =  sam_hdr_read(fp);

    int i = 0 ;

    do{

        bam1_t  *bam =  &bamVect_.a[i] ;
        if( sam_read1(fp,h,bam) < 0 ) {
            break ; 
        }

        
        bamVect_.n++ ;

        if( bamVect_.m == bamVect_.n ){
            bamVect_.m = bamVect_.m << 1 ;
            bamVect_.a =  realloc(bamVect_.a ,  sizeof(bam1_t)*bamVect_.m);
        }
        i++ ;

    }while(1);

    bam_hdr_destroy(h);
    sam_close(fp);
}

int  compared(const void *a_ ,const void *b_ )
{

    const bam1_t *a = a_ ;
    const bam1_t *b = b_ ;
    return  (((uint64_t)a->core.tid<<32|(a->core.pos+1)<<1|bam_is_rev(a)) < ((uint64_t)b->core.tid<<32|(b->core.pos+1)<<1|bam_is_rev(b))); 
}


void  sortVect()
{
    qsort(bamVect_.a,kv_size(bamVect_),sizeof(bam1_t),compared);
}

void  outputBam( char *fn , int n_threads )
{

    int i = 0 ;

    samFile *fp =  sam_open(fn,"wb");

    hts_set_threads(fp,n_threads);

    if(sam_hdr_write(fp,h_) < 0 ){
        fprintf(stderr,"[Error] Write Bam's Header\n");
        return  ;
    }

    for( i = 0 ; i <  kv_size(bamVect_) ; i++ ){
        bam1_t *item =  &kv_A(bamVect_,i);
        if(sam_write1(fp,h_,item) < 0){
            fprintf(stderr,"[Error] Write Bams\n");
            break ;
        }
    }
    sam_close(fp);
}


//  strategy two:


typedef struct {

    uint64_t coordinate;
    long offset ;
    int  tid ;

} BamOffset_t ;

typedef kvec_t(BamOffset_t) bamOffsetVect ; 

bamOffsetVect  bamOffsetVect_ ;

void  coreInit1( char *fn)
{
    samFile *fp =  sam_open(fn,"r");
    h_ =  sam_hdr_read(fp);
    sam_close(fp);
    kv_init(bamOffsetVect_);
}

void  readBamOffset2Vector( char *fn , int  tid , int n_threads)
{


    samFile  *fp =  sam_open(fn,"r");

    hts_set_threads(fp,n_threads);

    bam_hdr_t *h =  sam_hdr_read(fp);
    bam1_t *b  =  bam_init1(); 


    long offset = bgzf_tell(fp->fp.bgzf);

    int n =  0 ;
  
    while(  sam_read1(fp,h,b) >= 0 ){
        BamOffset_t *offset_ = kv_pushp( BamOffset_t , bamOffsetVect_);
        offset_->tid =  tid ;
        offset_->coordinate =   ((uint64_t)b->core.tid<<32|(b->core.pos+1)<<1|bam_is_rev(b)) ;
        offset_->offset = offset   ;
        offset = bgzf_tell(fp->fp.bgzf);
         n++ ;
        if(n % (1024*1024) == 0 ){
            fprintf(stderr,"read:%u\n",n);
        }
    }
   fprintf(stderr,"read:%u\n",n);
   
    bam_hdr_destroy(h);
    bam_destroy1(b);
    sam_close(fp);
}


int  sortPaired(const  void * a  ,const  void *b )
{
    const BamOffset_t  *a_ =  (BamOffset_t *) a ;
    const BamOffset_t  *b_ =  (BamOffset_t *) b ;
    return  b_->coordinate  - a_->coordinate  ;
}

void  sortVect1( char *fn , char *outPut )
{
//    qsort( bamOffsetVect_.a , kv_size(bamOffsetVect_), sizeof(BamOffset_t), sortPaired);
}

void write_( char **fns , int n_input  ,  char *outPut , int n_threads)
{
    samFile **fps =  malloc(sizeof(samFile *)*n_input);

    samFile *fout =  sam_open(outPut,"wb");
    int i = 0 ;

    for(  i =  0 ; i < n_input ; i++ ){
        fps[i] = sam_open(fns[i],"r");
        //hts_set_threads(fps[i],n_threads);

    }
    bam1_t *b =  bam_init1();

    hts_set_threads(fout,n_threads);
    for( i = 0 ; i  <  kv_size(bamOffsetVect_);i++){
        BamOffset_t item = kv_A(bamOffsetVect_,i);
        //item.coordinate 
        if(bgzf_seek(fps[item.tid]->fp.bgzf,item.offset , SEEK_SET) == -1 ){
            fprintf(stderr,"Seek Error  %s.\n",fns[item.tid]);
            break ;
        }

        if( sam_read1(fps[item.tid],h_,b) < 0 ) {
            fprintf(stderr,"Read Error\n");
            break ;
        }
        if(sam_write1(fout,h_,b) < 0 ){
            fprintf(stderr,"Write Error\n");
        }
        if(i % (1024*1024) == 0 ){
            fprintf(stderr,"Write: %u\n",i);
        }
    }

    bam_destroy1(b);
    sam_close(fout);
    for( i = 0 ; i < n_input ; i++ )
        sam_close(fps[i]);
    
    free(fps);
}



void coreDestory1()
{
    kv_destroy(bamOffsetVect_);
    bam_hdr_destroy(h_);
}



